/*     */ package com.zimbra.cs.store;
/*     */ 
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.stats.Counter;
/*     */ import com.zimbra.common.util.FileCache;
/*     */ import com.zimbra.common.util.FileCache.Item;
/*     */ import com.zimbra.common.util.FileUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.LogFactory;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.Server;
/*     */ import java.io.File;
/*     */ import java.io.FileInputStream;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Collections;
/*     */ import java.util.Iterator;
/*     */ import java.util.LinkedHashMap;
/*     */ import java.util.List;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ import java.util.zip.GZIPInputStream;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class FileDescriptorCache
/*     */ {
/*  50 */   private static final Log sLog = LogFactory.getLog(FileDescriptorCache.class);
/*     */   
/*     */ 
/*  53 */   private final LinkedHashMap<String, SharedFile> mCache = new LinkedHashMap(16, 0.75F, true);
/*     */   
/*  55 */   private final List<SharedFileInfo> mInactiveCache = Collections.synchronizedList(new ArrayList());
/*  56 */   private int mMaxSize = 1000;
/*     */   private final FileCache<String> mUncompressedFileCache;
/*  58 */   private final Counter mHitRate = new Counter();
/*     */   
/*     */   private class SharedFileInfo {
/*     */     public String path;
/*     */     public SharedFile file;
/*     */     
/*     */     public SharedFileInfo(String path, SharedFile file) {
/*  65 */       this.path = path;
/*  66 */       this.file = file;
/*     */     }
/*     */   }
/*     */   
/*     */   public FileDescriptorCache(FileCache<String> uncompressedCache) {
/*  71 */     this.mUncompressedFileCache = uncompressedCache;
/*     */   }
/*     */   
/*     */   public synchronized FileDescriptorCache setMaxSize(int maxSize) {
/*  75 */     if (maxSize < 0) {
/*  76 */       throw new IllegalArgumentException("maxSize value of " + maxSize + " is invalid (must be at least 0)");
/*     */     }
/*  78 */     this.mMaxSize = maxSize;
/*  79 */     this.mHitRate.reset();
/*  80 */     pruneIfNecessary();
/*  81 */     return this;
/*     */   }
/*     */   
/*     */   public FileDescriptorCache loadSettings() throws ServiceException {
/*  85 */     Server server = Provisioning.getInstance().getLocalServer();
/*  86 */     int fileDescriptorCacheSize = server.getMailFileDescriptorCacheSize();
/*     */     
/*  88 */     sLog.info("Loading settings: %s=%d.", new Object[] { "zimbraMailFileDescriptorCacheSize", Integer.valueOf(fileDescriptorCacheSize) });
/*     */     
/*     */ 
/*  91 */     setMaxSize(fileDescriptorCacheSize);
/*     */     
/*  93 */     return this;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   public synchronized void shutdown()
/*     */   {
/* 101 */     Iterator<Map.Entry<String, SharedFile>> iEntries = this.mCache.entrySet().iterator();
/* 102 */     while (iEntries.hasNext()) {
/* 103 */       Map.Entry<String, SharedFile> entry = (Map.Entry)iEntries.next();
/* 104 */       String path = (String)entry.getKey();
/* 105 */       SharedFile file = (SharedFile)entry.getValue();
/* 106 */       iEntries.remove();
/*     */       try {
/* 108 */         boolean success = close(file, path);
/* 109 */         if (!success)
/* 110 */           sLog.warn("Unable to close %s. File is in use.", new Object[] { file });
/*     */       } catch (IOException e) {
/* 112 */         sLog.warn("Unable to close %s", file, e);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   public int read(String path, long rawSize, long fileOffset, byte[] buf, int bufferOffset, int len)
/*     */     throws IOException
/*     */   {
/* 122 */     sLog.debug("Reading %s.  rawSize=%d, fileOffset=%d, bufferOffset=%d, len=%d.", new Object[] { path, Long.valueOf(rawSize), Long.valueOf(fileOffset), Integer.valueOf(bufferOffset), Integer.valueOf(len) });
/* 123 */     SharedFile file = null;
/*     */     int numRead;
/*     */     try
/*     */     {
/* 127 */       file = getSharedFile(path, rawSize);
/* 128 */       numRead = file.read(fileOffset, buf, bufferOffset, len);
/*     */     } finally {
/* 130 */       if (file != null) {
/* 131 */         file.doneReading();
/*     */       }
/*     */     }
/*     */     
/* 135 */     return numRead;
/*     */   }
/*     */   
/*     */   /* Error */
/*     */   boolean contains(String path)
/*     */   {
/*     */     // Byte code:
/*     */     //   0: aload_0
/*     */     //   1: dup
/*     */     //   2: astore_2
/*     */     //   3: monitorenter
/*     */     //   4: aload_0
/*     */     //   5: getfield 5	com/zimbra/cs/store/FileDescriptorCache:mCache	Ljava/util/LinkedHashMap;
/*     */     //   8: aload_1
/*     */     //   9: invokevirtual 58	java/util/LinkedHashMap:containsKey	(Ljava/lang/Object;)Z
/*     */     //   12: aload_2
/*     */     //   13: monitorexit
/*     */     //   14: ireturn
/*     */     //   15: astore_3
/*     */     //   16: aload_2
/*     */     //   17: monitorexit
/*     */     //   18: aload_3
/*     */     //   19: athrow
/*     */     // Line number table:
/*     */     //   Java source line #139	-> byte code offset #0
/*     */     //   Java source line #140	-> byte code offset #4
/*     */     //   Java source line #141	-> byte code offset #15
/*     */     // Local variable table:
/*     */     //   start	length	slot	name	signature
/*     */     //   0	20	0	this	FileDescriptorCache
/*     */     //   0	20	1	path	String
/*     */     //   2	15	2	Ljava/lang/Object;	Object
/*     */     //   15	4	3	localObject1	Object
/*     */     // Exception table:
/*     */     //   from	to	target	type
/*     */     //   4	14	15	finally
/*     */     //   15	18	15	finally
/*     */   }
/*     */   
/*     */   private SharedFile getSharedFile(String path, long rawSize)
/*     */     throws IOException
/*     */   {
/* 149 */     SharedFile sharedFile = null;
/*     */     
/* 151 */     synchronized (this) {
/* 152 */       sharedFile = (SharedFile)this.mCache.get(path);
/*     */     }
/* 154 */     if (sharedFile != null) {
/* 155 */       sLog.debug("Found existing file descriptor for %s, rawSize=%d.", new Object[] { path, Long.valueOf(rawSize) });
/* 156 */       sharedFile.aboutToRead();
/* 157 */       this.mHitRate.increment(100L);
/* 158 */       return sharedFile;
/*     */     }
/*     */     
/*     */ 
/* 162 */     this.mHitRate.increment(0L);
/* 163 */     File file = new File(path);
/*     */     
/* 165 */     if ((file.length() != rawSize) && (FileUtil.isGzipped(file))) {
/* 166 */       sLog.debug("Adding file descriptor cache entry for %s from the uncompressed file cache.", new Object[] { path });
/* 167 */       FileCache.Item uncompressed = this.mUncompressedFileCache.get(path);
/* 168 */       if (uncompressed == null) {
/* 169 */         InputStream in = null;
/* 170 */         in = new GZIPInputStream(new FileInputStream(file));
/* 171 */         this.mUncompressedFileCache.put(path, in);
/* 172 */         uncompressed = this.mUncompressedFileCache.get(path);
/* 173 */         if (uncompressed == null)
/*     */         {
/*     */ 
/* 176 */           throw new IOException("Unable to get uncompressed file for " + path);
/*     */         }
/*     */       }
/* 179 */       sharedFile = new SharedFile(uncompressed.file);
/*     */     } else {
/* 181 */       sLog.debug("Opening new file descriptor for %s.", new Object[] { path });
/* 182 */       sharedFile = new SharedFile(file);
/*     */     }
/*     */     
/* 185 */     synchronized (this) {
/* 186 */       if (this.mCache.containsKey(path)) {
/* 187 */         sLog.debug("Another thread just opened the same file.  Closing our copy and returning the other one.");
/* 188 */         sharedFile.close();
/* 189 */         sharedFile = (SharedFile)this.mCache.get(path);
/*     */       } else {
/* 191 */         sLog.debug("Caching file descriptor: path=%s, sharedFile=%s", new Object[] { path, sharedFile });
/* 192 */         this.mCache.put(path, sharedFile);
/*     */       }
/*     */     }
/* 195 */     sharedFile.aboutToRead();
/* 196 */     pruneIfNecessary();
/*     */     
/* 198 */     return sharedFile;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   public void remove(String path)
/*     */     throws IOException
/*     */   {
/* 206 */     SharedFile file = null;
/*     */     
/* 208 */     synchronized (this) {
/* 209 */       file = (SharedFile)this.mCache.remove(path);
/*     */     }
/*     */     
/* 212 */     if (file != null) {
/* 213 */       boolean success = close(file, path);
/* 214 */       if (!success)
/* 215 */         this.mInactiveCache.add(new SharedFileInfo(path, file));
/*     */     } else {
/* 217 */       sLog.debug("Attempted to remove %s but could not find it in the cache.", new Object[] { path });
/*     */     }
/*     */     
/*     */ 
/* 221 */     quietCloseInactiveCache();
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   private boolean close(SharedFile file, String path)
/*     */     throws IOException
/*     */   {
/* 230 */     if (file != null) {
/* 231 */       sLog.debug("Closing file descriptor for %s, %s", new Object[] { path, file });
/*     */       
/* 233 */       if (file.getNumReaders() == 0) {
/* 234 */         file.close();
/* 235 */         if (this.mUncompressedFileCache != null) {
/* 236 */           synchronized (this) {
/* 237 */             if (!this.mCache.containsKey(path)) {
/* 238 */               this.mUncompressedFileCache.remove(path);
/*     */             } else {
/* 240 */               sLog.debug("Not removing %s from the uncompressed cache.  Another thread reopened it.");
/*     */             }
/*     */           }
/*     */         }
/* 244 */         return true;
/*     */       }
/* 246 */       return false;
/*     */     }
/* 248 */     return true;
/*     */   }
/*     */   
/*     */   private void quietCloseInactiveCache() {
/* 252 */     synchronized (this.mInactiveCache) {
/* 253 */       Iterator<SharedFileInfo> iter = this.mInactiveCache.iterator();
/* 254 */       while (iter.hasNext()) {
/* 255 */         SharedFileInfo info = (SharedFileInfo)iter.next();
/*     */         try {
/* 257 */           boolean success = close(info.file, info.path);
/* 258 */           if (success)
/* 259 */             iter.remove();
/*     */         } catch (IOException e) {
/* 261 */           ZimbraLog.store.warn("Unable to close file descriptor for " + info.path, e);
/* 262 */           iter.remove();
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   public synchronized int getSize() {
/* 269 */     return this.mCache.size();
/*     */   }
/*     */   
/*     */   public double getHitRate() {
/* 273 */     return this.mHitRate.getAverage();
/*     */   }
/*     */   
/*     */   private void pruneIfNecessary() {
/* 277 */     if (getSize() <= this.mMaxSize) {
/* 278 */       return;
/*     */     }
/* 280 */     List<Map.Entry<String, SharedFile>> removeList = new ArrayList();
/*     */     
/* 282 */     synchronized (this) {
/* 283 */       Iterator<Map.Entry<String, SharedFile>> iEntries = this.mCache.entrySet().iterator();
/* 284 */       while ((iEntries.hasNext()) && (this.mCache.size() > this.mMaxSize)) {
/* 285 */         Map.Entry<String, SharedFile> mapEntry = (Map.Entry)iEntries.next();
/* 286 */         iEntries.remove();
/* 287 */         removeList.add(mapEntry);
/*     */       }
/*     */     }
/* 290 */     for (Map.Entry<String, SharedFile> mapEntry : removeList) {
/* 291 */       String path = (String)mapEntry.getKey();
/* 292 */       SharedFile file = (SharedFile)mapEntry.getValue();
/*     */       try {
/* 294 */         boolean success = close(file, path);
/* 295 */         if (!success) {
/* 296 */           this.mInactiveCache.add(new SharedFileInfo(path, file));
/*     */         }
/*     */       } catch (IOException e) {
/* 299 */         ZimbraLog.store.warn("Unable to close file descriptor for " + path, e);
/*     */       }
/*     */     }
/*     */     
/*     */ 
/* 304 */     quietCloseInactiveCache();
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/store/FileDescriptorCache.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */